package com.example.demo.aspect;

import com.example.demo.exceptions.AccessDeniedException;
import com.example.demo.model.CheckRole;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;

@Aspect
@Component
public class RbacAspect {

    private static final Logger logger = LoggerFactory.getLogger(RbacAspect.class);

    @Before("@annotation(com.example.demo.model.CheckRole)")
    public void checkRole(JoinPoint joinPoint) {
        // 获取方法签名，以便提取方法和其注解信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取方法上的CheckRole注解，以确定所需的角色
        CheckRole checkRole = signature.getMethod().getAnnotation(CheckRole.class);

        // 获取当前安全上下文中的认证信息
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        // 如果没有认证信息，表示用户未认证，记录警告并抛出异常
        if (auth == null) {
            logger.warn("用户未认证。");
            throw new AccessDeniedException("用户未认证");
        }

        // 提取用户的所有角色，并转换为角色名称集合
        Set<String> authorities = auth.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toSet());

        // 检查用户是否具有访问方法所需的一个或多个角色
        boolean hasRequiredRole = false;
        for (String requiredRole : checkRole.value()) {
            if (authorities.contains(requiredRole)) {
                hasRequiredRole = true;
                break;
            }
        }

        if (!hasRequiredRole) {
            // 如果没有所需角色，记录警告并抛出异常
            try {
                logger.warn("用户没有足够的权限访问方法: {}", signature.getName());
                throw new AccessDeniedException("权限不足");
            } catch (AccessDeniedException e) {
                logger.error("抛出异常: {}", e.getClass().getName(), e);
                throw e; // 重新抛出异常
            }
        }

        // 如果用户具有所需角色，记录信息
        logger.info("用户具有访问方法所需的权限: {}", signature.getName());
    }
}